﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.Design;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.TextTemplating.VSHost;
using EnvDTE;
using EnvDTE80;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using VSLangProj;
using System.IO;

namespace Planet.TransformDslExt
{
    internal sealed class TransformCommandSet
    {
        private IServiceProvider _serviceProvider;
        private DTE2 _dte;

        public TransformCommandSet(IServiceProvider serviceProvider)
        {
            this._serviceProvider = serviceProvider;
            _dte = _serviceProvider.GetService(typeof(DTE)) as DTE2;

            OleMenuCommandService mcs = _serviceProvider.GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
            if (mcs != null)
            {
                CommandID trsfrmSelCmdID = new CommandID(GuidList.guidTransformDslExtCmdSet, (int)PkgCmdIDList.cmdidTransformSelected);
                var trsfrlSelMenu = new OleMenuCommand(this.OnTransformSelectedExec, trsfrmSelCmdID);
                trsfrlSelMenu.BeforeQueryStatus += new EventHandler(OnTransformSelectedStatus);
                mcs.AddCommand(trsfrlSelMenu);

                CommandID trsfrmSelectionCmdID = new CommandID(GuidList.guidTransformDslExtCmdSet, (int)PkgCmdIDList.cmdidTransformSelection);
                var trsfrlSelectionMenu = new OleMenuCommand(this.OnTransformSelectionExec, trsfrmSelectionCmdID);
                trsfrlSelectionMenu.BeforeQueryStatus += new EventHandler(OnTransformSelectedStatus);
                mcs.AddCommand(trsfrlSelectionMenu);
            }
        }

        #region Transform Selected Events

        void OnTransformSelectedStatus(object sender, EventArgs e)
        {
            MenuCommand command = sender as MenuCommand;
            if (command != null)
            {
                command.Visible = command.Enabled = this.IsSelectedProjectValable();
            }
        }

        void OnTransformSelectedExec(object sender, EventArgs e)
        {
            this.SaveAllDocumentsBeforeBuild();

            _dte.ExecuteCommand("View.Output", "");
            OutputWindowHelper outHelper = new OutputWindowHelper(_dte);
            List<ProjectItem> toProcess = new List<ProjectItem>();
            
            //Get items to be transformed.
            Project selectedProject = VSHelper.ToDteProject(VSHelper.GetSelectedProject(_serviceProvider));
            VSHelper.FindProjectItems(selectedProject, toProcess);
            outHelper.Clear();
            outHelper.AddMessage(string.Format(Resources.StartCodeGen, selectedProject.Name));
            outHelper.AddMessage(Resources.Seperator);

            uint pdwCookie = 0;
            uint count = (uint)toProcess.Count;
            IVsStatusbar service = _serviceProvider.GetService(typeof(IVsStatusbar)) as IVsStatusbar;
            if (service != null)
            {
                service.Progress(ref pdwCookie, 1, Resources.TextTemplatingStatusBarLabel, 0, count);
            }

            uint complete = 1;
            bool errorFlag = false;
            foreach (ProjectItem item in toProcess)
            {
                Application.DoEvents();
                VSProjectItem vsItem = item.Object as VSProjectItem;
                if (vsItem != null)
                {
                    errorFlag |= this.RunCustomTool(item, vsItem, VSHelper.GetCustomTool(item), outHelper);
                }
                if (service != null)
                {
                    service.Progress(ref pdwCookie, 1, Resources.TextTemplatingStatusBarLabel, complete, count);
                }
                complete++;
                Application.DoEvents();
            }

            outHelper.AddMessage(Resources.Seperator);
            outHelper.AddMessage(Resources.EndCodeGen);
            if (service != null)
            {
                service.Progress(ref pdwCookie, 0, string.Empty, count, count);
            }
        }

        #endregion

        #region Transform Selection Events

        void OnTransformSelectionExec(object sender, EventArgs e)
        {
            this.SaveAllDocumentsBeforeBuild();

        }

        #endregion

        #region Private Methods

        private bool IsSelectedProjectValable()
        {
            IVsHierarchy projectHierarchy = VSHelper.GetSelectedProject(_serviceProvider);
            if (projectHierarchy != null)
            {
                IVsProjectStartupServices startup = GetStartUpServices(projectHierarchy);
                if ((startup != null) && StartUpServicesReferencesService(startup, typeof(STextTemplating).GUID))
                {
                    return true;
                }
            }

            return false;
        }

        private IVsProjectStartupServices GetStartUpServices(IVsHierarchy hierarchy)
        {
            object obj;
            hierarchy.GetProperty(0xfffffffe, -2040, out obj);
            if (obj != null)
            {
                return obj as IVsProjectStartupServices;
            }

            return null;
        }

        //TODO refactor this implementation. Can be done better.
        private bool StartUpServicesReferencesService(IVsProjectStartupServices startup, Guid serviceId)
        {
            int num;
            IEnumProjectStartupServices psenum = null;
            startup.GetStartupServiceEnum(out psenum);
            uint fetched = 0;
            Guid[] result = new Guid[1];
            do
            {
                num = psenum.Next(1, result, out fetched);
                if ((fetched == 1) && (result[0].CompareTo(serviceId) == 0))
                    return true;
            }
            while (num != 1);
            return false;
        }

        private bool RunCustomTool(ProjectItem item, VSProjectItem vsItem, string customTool, OutputWindowHelper outHelper)
        {
            bool flag = true;
            bool flag2 = false;
            try
            {
                string text1 = (string)item.Properties.Item("FullPath").Value;
            }
            catch (ArgumentException)
            {
                flag = false;
                outHelper.AddMessage(string.Format(Resources.SkippingNoPath, new object[] { item.Name }));
            }
            if ((flag && (item.ProjectItems != null)) && (item.ProjectItems.Count > 0))
            {
                foreach (ProjectItem item2 in item.ProjectItems)
                {
                    string str = "";
                    try
                    {
                        str = (string)item2.Properties.Item("FullPath").Value;
                    }
                    catch (ArgumentException)
                    {
                        flag = false;
                        outHelper.AddMessage(string.Format(Resources.SkippingNoPath, new object[] { item2.Name }));
                    }
                    if ((!string.IsNullOrEmpty(str) && !_dte.SourceControl.IsItemUnderSCC(str)) && (File.Exists(str) && ((File.GetAttributes(str) & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)))
                    {
                        flag = false;
                        outHelper.AddMessage(string.Format(Resources.SkippingReadOnly, new object[] { str }));
                    }
                }
            }
            if (flag)
            {
                outHelper.AddMessage(string.Format(Resources.RunLine, new object[] { customTool, item.Name }));
                try
                {
                    vsItem.RunCustomTool();
                    outHelper.Add(Resources.LineSucceeded);
                }
                catch
                {
                    outHelper.Add(Resources.LineFailed);
                    flag2 = true;
                }
            }
            return flag2;
        }

        private void SaveAllDocumentsBeforeBuild()
        {
            if ((_dte != null) && (_dte.Solution != null) && (_dte.Solution.Projects != null))
            {
                uint itemid = 0xfffffffe;
                try
                {
                    IVsSolutionBuildManager2 manager = _serviceProvider.GetService(typeof(SVsSolutionBuildManager)) as IVsSolutionBuildManager2;
                    if (manager != null)
                    {
                        manager.SaveDocumentsBeforeBuild(null, itemid, 0);
                    }
                }
                catch (COMException exception)
                {
                    if ((exception.ErrorCode != -2147467260) && (exception.ErrorCode != -2147221492))
                    {
                        throw;
                    }
                    return;
                }
            }
            Application.DoEvents();
        }

        #endregion
    }
}
